package com.run2code.idea.plugin.codegenerate.ui;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectUtil;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.run2code.idea.plugin.codegenerate.biz.db.content.DbConfig;
import com.run2code.idea.plugin.codegenerate.biz.db.metadata.Table;
import com.run2code.idea.plugin.codegenerate.biz.db.utils.DBUtils;
import com.run2code.idea.plugin.codegenerate.biz.generate.content.CodeGenContext;
import com.run2code.idea.plugin.codegenerate.biz.generate.template.CodeGeneratorFactory;
import com.run2code.idea.plugin.codegenerate.component.FileChooserComponent;
import com.run2code.idea.plugin.codegenerate.data.DataSetting;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 代码生成工具-窗口UI
 * 关注公众号【奔跑的码畜】，一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/10/27
 */
public class CodeGenerateUi {
    /**
     * 类路径
     */
    private JTextField classpath;
    /**
     * 项目名称
     */
    private JTextField projectName;
    /**
     * 模板地址
     */
    private JTextField templateUrl;
    /**
     * 选择模板地址的按钮
     */
    private JButton templateUrlButton;
    /**
     * 选择entity生成地址的按钮
     */
    private JButton entityButton;
    /**
     * mapper生成地址
     */
    private JTextField mapperUrl;
    /**
     * 选择mapper生成地址的按钮
     */
    private JButton mapperUrlButton;
    /**
     * xml生成地址
     */
    private JTextField xmlUrl;
    /**
     * 选择xml生成地址的按钮
     */
    private JButton xmlUrlButton;
    /**
     * 整个大的框架
     */
    private JPanel main;
    /**
     * entity生成地址
     */
    private JTextField entityUrl;
    /**
     * 表数据
     */
    private JTable table1;
    /**
     * 表端口
     */
    private JTextField port;
    /**
     * 表host
     */
    private JTextField localhost;
    /**
     * 表数据库
     */
    private JTextField database;
    /**
     * 用户名
     */
    private JTextField username;
    /**
     * 密码
     */
    private JPasswordField password;
    /**
     * 测试按钮
     */
    private JButton testButton;
    /**
     * 查询按钮
     */
    private JButton queryButton;
    /**
     * 生成按钮
     */
    private JButton generateButton;
    /**
     * 表名 - 单个表名用于快速的模糊查询
     */
    private JTextField tablename;

    /**
     * 工程 - 这个指的是IDEA的工程
     */
    private Project project;

    /**
     * 表名集合缓存
     */
    Set<String> tableNames;

    /**
     * db配置上下文
     */
    private DbConfig config;

    /**
     * 可以理解为在这里初始化插件
     *
     * @param project 工程
     */
    public CodeGenerateUi(Project project) {
        // 初始化数据
        this.project = project;
        this.projectName.setText(project.getName());
        this.classpath.setText(project.getBasePath());
        config = DataSetting.getInstance(project).getDbConfig();

        this.database.setText(config.getDatabase());
        this.localhost.setText(config.getHost());
        this.port.setText(config.getPort());
        this.entityUrl.setText(config.getEntityPath());
        this.mapperUrl.setText(config.getMapperPath());
        this.xmlUrl.setText(config.getXmlPath());
        this.username.setText(config.getUser());

        // 增加监听
        addListener(project);
    }

    /**
     * 增加监听
     *
     * @param project 工程
     */
    private void addListener(Project project) {
        // 选择自定义模版目录
        listenFileChooserButton(this.templateUrlButton, project, "选择自定义模版目录", CodeGenerateUi.this.templateUrl);

        // 选择entity生成目录
        listenFileChooserButton(this.entityButton, project, "选择entity生成目录", CodeGenerateUi.this.entityUrl);

        // 选择mapper生成目录
        listenFileChooserButton(this.mapperUrlButton, project, "选择mapper生成目录", CodeGenerateUi.this.mapperUrl);

        // 选择xml生成目录
        listenFileChooserButton(this.xmlUrlButton, project, "选择xml生成目录", CodeGenerateUi.this.xmlUrl);

        // 测试数据库链接
        listenTestButton();

        // 查询数据库表列表
        listenQueryButton();

        // 监听鼠标点击表格
        listenTableMouseClicked();

        // 生成代码
        listenGenerateButton();

    }

    /**
     * 监听鼠标点击表格
     */
    private void listenTableMouseClicked() {
        this.table1.addMouseListener(new MouseAdapter() {
            // 选择表
            @Override
            public void mouseClicked(MouseEvent e) {
                if (1 == e.getClickCount()) {
                    int rowIdx = table1.rowAtPoint(e.getPoint());
                    Boolean flag = (Boolean) table1.getValueAt(rowIdx, 0);
                    if (null != flag && flag) {
                        tableNames.add(table1.getValueAt(rowIdx, 1).toString());
                    } else {
                        tableNames.remove(table1.getValueAt(rowIdx, 1).toString());
                    }
                }
            }
        });
    }

    /**
     * 监听查询数据库表按钮
     */
    private void listenQueryButton() {
        try {
            this.queryButton.addActionListener(e -> {
                if (checkRequireDbParam(project)) {
                    return;
                }
                if (this.database.getText() == null || "".equals(this.database.getText())) {
                    Messages.showWarningDialog(project, "请填写database", "Warring");
                    return;
                }
                // 查询的时候需要先把缓存置空
                clearCache();
                // 查询数据库表
                List<String> tableList = getTableByDb();
                // 画表
                drawTable(tableList);
            });
        } catch (Exception exception) {
            Messages.showWarningDialog(project, "数据库查询错误,请检查配置.", "Error");
        }
    }


    private void clearCache() {
        // 清除UI缓存
        this.tableNames = new HashSet<>();
        CodeGenerateUi.this.config.setTableNames(this.tableNames);
    }

    private List<String> getTableByDb() {
        DBUtils dbUtil = new DBUtils(this.localhost.getText(), Integer.parseInt(this.port.getText()), this.username.getText(), String.valueOf(this.password.getPassword()), this.database.getText());
        return !"".equals(this.tablename.getText())
                // 模糊查询表名
                ? dbUtil.getResembleTableName(this.tablename.getText())
                // 查询所有的表名
                : dbUtil.getAllTableName(this.database.getText());
    }

    private void drawTable(List<String> tableList) {
        String[] title = {"序号", "表名"};
        Object[][] data = new Object[tableList.size()][2];
        for (int i = 0; i < tableList.size(); i++) {
            data[i][1] = tableList.get(i);
        }

        table1.setModel(new DefaultTableModel(data, title));
        TableColumn tc = table1.getColumnModel().getColumn(0);
        tc.setCellRenderer(table1.getDefaultRenderer(Boolean.class));
        tc.setCellEditor(new DefaultCellEditor(new JCheckBox()));
        tc.setCellEditor(table1.getDefaultEditor(Boolean.class));
        tc.setMaxWidth(200);
    }

    /**
     * 监听代码生成按钮
     */
    private void listenGenerateButton() {
        this.generateButton.addActionListener(e -> {
            try {
                if (checkRequireGenerateParam(project)) {
                    return;
                }
                // 生成代码
                // 设置配置
                config.setUser(this.username.getText());
                config.setPassword(new String(this.password.getPassword()));
                config.setProjectName(this.projectName.getText());
                config.setClasspath(this.classpath.getText());
                config.setDatabase(this.database.getText());
                config.setHost(this.localhost.getText());
                config.setPort(this.port.getText() != null ? this.port.getText() : "3306");
                config.setEntityPath(this.entityUrl.getText());
                config.setMapperPath(this.mapperUrl.getText());
                config.setXmlPath(this.xmlUrl.getText());
                config.setTemplateUrl(this.templateUrl.getText());

                // 组装代码生产上下文
                CodeGenContext codeGenContext = new CodeGenContext();
                codeGenContext.setEntityPackage(config.getEntityPath() + "/");
                codeGenContext.setMapperPackage(config.getMapperPath() + "/");
                codeGenContext.setXmlDir(config.getXmlPath() + "/");
                codeGenContext.setTemplateUrl(config.getTemplateUrl());

                // 链接DB
                DBUtils dbUtil = new DBUtils(config.getHost(), Integer.parseInt(config.getPort()), config.getUser(), config.getPassword(), config.getDatabase());
                List<Table> tables = new ArrayList<>();
                for (String tableName : tableNames) {
                    tables.add(dbUtil.getTable(tableName));
                }
                codeGenContext.setTables(tables);

                // 这里必须要放到UI的线程中，不然打包成插件后会报错
                ApplicationManager.getApplication().runWriteAction(() ->
                        CodeGeneratorFactory.instance(codeGenContext).generation(project, codeGenContext));

                Messages.showWarningDialog(project, "代码已生成", "Success");
            } catch (Exception ex) {
                Messages.showWarningDialog(project, "代码生成失败：" + ex.getMessage(), "Error");
            }
        });
    }

    /**
     * 校验代码生成参数
     *
     * @param project
     * @return boolean
     */
    private boolean checkRequireGenerateParam(Project project) {
        if (checkRequireDbParam(project)) {
            return true;
        }
        if (tableNames == null || tableNames.size() == 0) {
            Messages.showWarningDialog(project, "代码生成失败：请选择需要生成代码的数据库表！", "Error");
            return true;
        }
        if (this.database.getText() == null || "".equals(this.database.getText())) {
            Messages.showWarningDialog(project, "请填写database", "Warring");
            return true;
        }
        return false;
    }

    /**
     * 校验Db必填参数
     *
     * @param project
     * @return boolean
     */
    private boolean checkRequireDbParam(Project project) {
        if (this.username.getText() == null || "".equals(this.username.getText())) {
            Messages.showWarningDialog(project, "请填写用户名", "Warring");
            return true;
        }
        if (this.password.getPassword() == null || this.password.getPassword().length == 0) {
            Messages.showWarningDialog(project, "请填写密码", "Warring");
            return true;
        }
        if (this.port.getText() == null || "".equals(this.port.getText())) {
            Messages.showWarningDialog(project, "请填写端口号", "Warring");
            return true;
        }
        if (this.localhost.getText() == null || "".equals(this.localhost.getText())) {
            Messages.showWarningDialog(project, "请填写数据库连接地址", "Warring");
            return true;
        }
        return false;
    }

    /**
     * 监听测试按钮
     */
    private void listenTestButton() {
        this.testButton.addActionListener(e -> {
            // 测试数据库连接
            try {
                if (checkRequireDbParam(project)) {
                    return;
                }
                // 连接数据库
                DBUtils dbUtil = new DBUtils(this.localhost.getText(), Integer.parseInt(this.port.getText()), this.username.getText(), String.valueOf(this.password.getPassword()), this.database.getText());
                String mysqlVersion = dbUtil.testDatabase();
                Messages.showInfoMessage(project, "数据库连接成功! \r\n数据库版本: " + mysqlVersion, "连接成功");
            } catch (Exception exception) {
                Messages.showWarningDialog(project, "数据库连接错误,请检查配置." + exception.getMessage(), "Error");
            }
        });
    }

    /**
     * 监听文件选择按钮
     *
     * @param templateUrlButton
     * @param project
     * @param message
     */
    private void listenFileChooserButton(JButton templateUrlButton, Project project, String message, JTextField templateUrl) {
        templateUrlButton.addActionListener(e -> {
            FileChooserComponent component = FileChooserComponent.getInstance(project);
            VirtualFile baseDir = ProjectUtil.guessProjectDir(project);
            VirtualFile virtualFile = component.showFolderSelectionDialog(message, baseDir, baseDir);
            if (null != virtualFile) {
                templateUrl.setText(virtualFile.getPath());
            }
        });
    }

    public JButton getGenerateButton() {
        return generateButton;
    }

    public void setGenerateButton(JButton generateButton) {
        this.generateButton = generateButton;
    }

    public JTextField getClasspath() {
        return classpath;
    }

    public void setClasspath(JTextField classpath) {
        this.classpath = classpath;
    }

    public JTextField getProjectName() {
        return projectName;
    }

    public void setProjectName(JTextField projectName) {
        this.projectName = projectName;
    }

    public JTextField getTemplateUrl() {
        return templateUrl;
    }

    public void setTemplateUrl(JTextField templateUrl) {
        this.templateUrl = templateUrl;
    }

    public JButton getTemplateUrlButton() {
        return templateUrlButton;
    }

    public void setTemplateUrlButton(JButton templateUrlButton) {
        this.templateUrlButton = templateUrlButton;
    }

    public JButton getEntityButton() {
        return entityButton;
    }

    public void setEntityButton(JButton entityButton) {
        this.entityButton = entityButton;
    }

    public JTextField getMapperUrl() {
        return mapperUrl;
    }

    public void setMapperUrl(JTextField mapperUrl) {
        this.mapperUrl = mapperUrl;
    }

    public JButton getMapperUrlButton() {
        return mapperUrlButton;
    }

    public void setMapperUrlButton(JButton mapperUrlButton) {
        this.mapperUrlButton = mapperUrlButton;
    }

    public JTextField getXmlUrl() {
        return xmlUrl;
    }

    public void setXmlUrl(JTextField xmlUrl) {
        this.xmlUrl = xmlUrl;
    }

    public JButton getXmlUrlButton() {
        return xmlUrlButton;
    }

    public void setXmlUrlButton(JButton xmlUrlButton) {
        this.xmlUrlButton = xmlUrlButton;
    }

    public JPanel getMain() {
        return main;
    }

    public void setMain(JPanel main) {
        this.main = main;
    }

    public JTextField getEntityUrl() {
        return entityUrl;
    }

    public void setEntityUrl(JTextField entityUrl) {
        this.entityUrl = entityUrl;
    }

    public JTable getTable1() {
        return table1;
    }

    public void setTable1(JTable table1) {
        this.table1 = table1;
    }

    public JTextField getPort() {
        return port;
    }

    public void setPort(JTextField port) {
        this.port = port;
    }

    public JTextField getLocalhost() {
        return localhost;
    }

    public void setLocalhost(JTextField localhost) {
        this.localhost = localhost;
    }

    public JTextField getDatabase() {
        return database;
    }

    public void setDatabase(JTextField database) {
        this.database = database;
    }

    public JTextField getUsername() {
        return username;
    }

    public void setUsername(JTextField username) {
        this.username = username;
    }

    public JPasswordField getPassword() {
        return password;
    }

    public void setPassword(JPasswordField password) {
        this.password = password;
    }

    public JButton getTestButton() {
        return testButton;
    }

    public void setTestButton(JButton testButton) {
        this.testButton = testButton;
    }

    public JButton getQueryButton() {
        return queryButton;
    }

    public void setQueryButton(JButton queryButton) {
        this.queryButton = queryButton;
    }

    public Project getProject() {
        return project;
    }

    public void setProject(Project project) {
        this.project = project;
    }

    public JTextField getTablename() {
        return tablename;
    }

    public void setTablename(JTextField tablename) {
        this.tablename = tablename;
    }
}
